package jwt

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v4"
	log "github.com/sirupsen/logrus"
	"go-web-demo/biz/user/domain/req"
	"go-web-demo/config/cache"
	"go-web-demo/config/jwt/exception"
	e "go-web-demo/exception"
	"io"
	"io/ioutil"
	"strings"
)

// JwtVerify Jwt校验
func JwtVerify(c *gin.Context) {
	request := c.Request
	// 过滤不用Jwt校验的url
	if noJwtVerify(JwtCig.IgnorePaths, request.RequestURI) {
		return
	}

	// 获取token
	bearer := request.Header.Get("Authorization")
	if len(bearer) == 0 {
		panic(exception.NewJwtError(e.Unauthorized, e.GetResultMsg(e.Unauthorized)))
	}
	tokenStr := strings.Split(bearer, " ")[1]
	// 对secret用Base64解码
	secret, _ := base64.StdEncoding.DecodeString(JwtCig.Secret)
	token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}
		return secret, nil
	})

	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		// 校验用户一致性
		// 兼容RestFul
		// 获取url 内的Account，若为空
		// 则获取request body 内的userId
		var userId string
		if err := request.ParseForm(); err != nil {
			panic(err)
		}
		userId = request.FormValue("Account")
		if len(userId) == 0 {
			var requestParams req.UserRequest
			defer func(Body io.ReadCloser) {
				err := Body.Close()
				if err != nil {
					panic(err)
				}
			}(request.Body)
			bodyBytes, err := ioutil.ReadAll(request.Body)
			if err := json.Unmarshal(bodyBytes, &requestParams); err != nil {
				panic(err)
			}
			if err != nil {
				panic(err)
			}
			c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
			userId = requestParams.Account
		}
		subject := claims["sub"].(string)
		userRole := strings.Split(subject, "_")[0]
		// 游客身份不支持调用支付服务
		if strings.Compare(userRole, "Guest") == 0 {
			panic(e.NewApiError(e.GuestNotPayErr, e.GetResultMsg(e.GuestNotPayErr)))
		}
		userIdOfToken := strings.Split(subject, "_")[1]
		if !strings.Contains(userId, userIdOfToken) {
			panic(exception.NewJwtError(e.JwtUserNotMatchErr, e.GetResultMsg(e.JwtUserNotMatchErr)))
		}
	} else {
		log.WithField("JwtError", err.Error()).Error()
		panic(exception.NewJwtError(e.Unauthorized, e.GetResultMsg(e.Unauthorized)))
	}
	c.Next()
}

// noJwtVerify 判断url是否不需要Jwt校验
func noJwtVerify(ignorePaths []string, path string) bool {
	// 查询缓存
	if noVerify, err := cache.BigCache.Get(path); err == nil {
		return noVerify.(bool)
	}
	// 匹配url
	for _, ignorePath := range ignorePaths {
		// 路径尾通配符*过滤
		if strings.LastIndex(ignorePath, "*") == len(ignorePath)-1 {
			ignorePath = strings.Split(ignorePath, "*")[0]
			if endIndex := strings.LastIndex(path, "/"); strings.Compare(path[0:endIndex+1], ignorePath) == 0 {
				// 添加缓存
				cache.BigCache.Set(path, true)
				return true
			}
			// 无通配符*过滤
		} else if strings.Compare(path, ignorePath) == 0 {
			// 添加缓存
			cache.BigCache.Set(path, true)
			return true
		}
	}
	return false
}
